package com.pkk.components.util.spring;

import com.alibaba.fastjson.JSONObject;
import com.pkk.components.util.spring.scan.InitRetryRabbitContainer;
import com.pkk.components.util.spring.scan.KeepAnnotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections4.MapUtils;

/**
 * @description:
 * @author: peikunkun
 * @create: 2019-06-15 17:52
 **/
public class SpringScanAnnotation {


  /**
   * 查询指定包中的类的所有注解
   *
   * @param scanPackagePath 指定包路径
   * @param annotation 需要查询的注解
   * @param onlyMethodContain 仅仅是在方法中包含此注解
   * @param checkRepeat 是否查重
   * @return 注解集合
   * @throws Exception 如果有两个方法上的两个注解完全相同时则抛出异常
   */
  public static List<KeepAnnotation> getAnnotationByPackage(String scanPackagePath
      , Class<? extends Annotation> annotation, boolean onlyMethodContain, boolean checkRepeat) throws Exception {
    Set<Class<?>> classes = InitRetryRabbitContainer.getClasses(scanPackagePath);
    List<KeepAnnotation> keepAnnotations = new ArrayList<>(classes.size());

    for (Class classzz : classes) {
      KeepAnnotation keepAnnotation = getAnnotationByClass(classzz, annotation, checkRepeat);
      if (onlyMethodContain) {
        if (!MapUtils.isEmpty(keepAnnotation.getMethodAnnotation())) {
          keepAnnotations.add(keepAnnotation);
        }
      } else {
        if (null != keepAnnotation.getClasszzAnnotation()) {
          keepAnnotations.add(keepAnnotation);
        }
      }
    }
    return keepAnnotations;
  }


  /**
   * 查询指定包中的类的所有注解
   *
   * @param scanPackagePath 指定包路径
   * @param annotation 需要查询的注解
   * @param checkRepeat 是否查重
   * @return 注解集合
   * @throws Exception 如果有两个方法上的两个注解完全相同时则抛出异常
   */
  public static List<KeepAnnotation> getAnnotationByPackage(String scanPackagePath
      , Class<? extends Annotation> annotation
      , boolean checkRepeat) throws Exception {

    Set<Class<?>> classes = InitRetryRabbitContainer.getClasses(scanPackagePath);
    List<KeepAnnotation> keepAnnotations = new ArrayList<>(classes.size());

    for (Class classzz : classes) {
      KeepAnnotation keepAnnotation = getAnnotationByClass(classzz, annotation, checkRepeat);
      keepAnnotations.add(keepAnnotation);
    }
    return keepAnnotations;
  }

  /**
   * 查找一个类上的所有注解
   *
   * @param classzz 指定查找的类
   * @param annotation 需要获取的注解
   * @param checkRepeat 是否查找重复的注解
   * @return 注解集合
   * @throws Exception 如果有两个方法上的两个注解完全相同时则抛出异常
   */
  public static KeepAnnotation getAnnotationByClass(Class<?> classzz
      , Class<? extends Annotation> annotation
      , boolean checkRepeat) throws Exception {

    KeepAnnotation KeepAnnotation = new KeepAnnotation(classzz);

    // 获取到类上的注解
    Annotation classzzAnnotation = classzz.getAnnotation(annotation);
    if (classzzAnnotation != null) {
      KeepAnnotation.setClasszzAnnotation(classzzAnnotation);
    }

    // 获取到方法上的注解
    Map<String, Annotation> methodAnnotations = new HashMap<>();
    Method[] methods = classzz.getMethods();
    for (Method method : methods) {
      Annotation methodAnnotation = method.getAnnotation(annotation);
      if (methodAnnotation != null) {
        // 如果需要检查重复则检查否则不检测注解值是否重复
        if (checkRepeat && methodAnnotations.containsValue(methodAnnotation)) {
          throw new Exception(classzz.getName() + " \n" + method.getName() + " \n"
              + methodAnnotation.toString() + " have repeat value");
        } else {
          methodAnnotations.put(method.getName(), methodAnnotation);
        }

      }
    }
    KeepAnnotation.setMethodAnnotation(methodAnnotations);
    return KeepAnnotation;
  }


  public static void main(String[] args) throws Exception {
    //获取特定类下面的注解
    //Set<Class<?>> classes = InitRetryRabbitContainer.getClasses("com.pkk");
    //KeepAnnotation KeepAnnotation = getAnnotationByClass(ExecSortController.class, InitRetryRabbitMq.class, false);
    //System.out.println(JSONObject.toJSONString(KeepAnnotation));

    //获取指定包下面的注解
    final List<KeepAnnotation> annotationByPackage = getAnnotationByPackage("com.pkk", Annotation.class, true,
        false);
    System.out.println(JSONObject.toJSONString(annotationByPackage));
  }


}
